/**
 * @file logger.hpp
 * @brief 实现日志宏函数LOG()
 * 日志消息格式: 
 * [时间 消息级别 进程ID 文件名:行号] 日志内容
 */
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <mutex>
#include <ctime>
#include <cstdarg>
#include <cstring>
#include <unistd.h>

/**
 * @enum LogLevel
 * @brief 定义日志等级枚举值
 * 0: DEBUG - 调试信息
 * 1: INFO  - 正常操作信息
 * 2: ERROR - 错误信息
 */
enum LogLevel {
    DEBUG = 0,
    INFO,
    ERROR
};

std::mutex g_mutex;                         // 保证多线程下日志宏的正常使用
size_t log_max_message_size = 512;          // 日志消息的最大长度
int minimum_log_level = INFO;                // 日志的最低日志级别
bool log_output_to_file = false;             // 是否输出日志到文件
const std::string LOG_FILENAME = "log.txt";  // 日志文件名

/**
 * @brief 获取当前时间的字符串表示
 * @return std::string 当前时间，格式为 "YYYY-MM-DD HH:MM:SS"
 */
static std::string get_time_string() {
    time_t cur_time = time(nullptr);               // 获取当前时间戳
    struct tm *format_time = localtime(&cur_time); // 转换为本地时间
    if (format_time == nullptr) {
        return "None";
    }
    char time_buffer[128]{0};                      // 格式化时间为字符串
    strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %T", format_time);
    return time_buffer;
}

/**
 * @brief 获取对应日志级别的字符串表示
 * @param level 日志级别 (DEBUG, INFO, ERROR)
 * @return std::string 日志级别对应的字符串
 */
static std::string get_level_string(const int level) {
    switch (level) {
    case DEBUG:     return "DEBUG";
    case INFO:      return "INFO";
    case ERROR:     return "ERROR";
    default:        return "UNKNOWN"; // 返回未知的日志级别
    }
}

/**
 * @brief 将日志消息追加写入到指定文件中
 * @param filename 目标文件名
 * @param message 日志内容
 */
static void output_to_file(const std::string &filename, const std::string &message) {
    std::ofstream out(filename, std::ios::out | std::ios::app);  // 以文本追加模式打开文件
    if (!out.is_open()) {
        std::cerr << "Error: Unable to open log file " << filename << std::endl;
        return;      // 如果文件无法打开，直接返回
    }
    out << message;  // 写入日志内容
    out.close();     // 关闭文件
}

/**
 * @brief 日志宏，用于格式化并输出日志信息
 * @param level 日志级别 (DEBUG, INFO, ERROR)
 * @param format 日志内容的格式化字符串
 * @param ... 可变参数，配合格式化字符串使用
 */
#define LOG(level, format, ...)                                             \
    do {                                                                    \
        if (level >= minimum_log_level) {                                   \
            std::string message(log_max_message_size, '\0');                \
            snprintf(&message[0], log_max_message_size,                     \
                    "[%s %s %d %s:%d] " format,                             \
                    get_time_string().c_str(),                              \
                    get_level_string(level).c_str(),                        \
                    getpid(),                                               \
                    __FILE__,                                               \
                    __LINE__,                                               \
                    ##__VA_ARGS__);                                         \
            {                                                               \
                std::unique_lock<std::mutex> lock(g_mutex);                 \
                if (log_output_to_file) {                                   \
                    message.resize(message.find_last_not_of('\0') + 1);     \
                    output_to_file(LOG_FILENAME, message);                  \
                } else {                                                    \
                    std::cout << message;                                   \
                }                                                           \
            }                                                               \
        }                                                                   \
    } while (0)

/**
 * @brief 初始化日志系统
 * @param is_output_to_file 是否将日志输出到文件
 * @param min_log_level 设置默认的日志输出级别 (DEBUG, INFO, ERROR)
 * @param max_message_size 设置日志消息的最大长度
 * 
 * 通过该宏可以初始化日志系统，配置是否将日志写入文件、设置最低日志级别和日志消息最大长度。
 */
#define LOG_INIT(is_output_to_file, min_log_level, max_message_size)    \
    do {                                                                \
        std::unique_lock<std::mutex> lock(g_mutex);                     \
        log_output_to_file = is_output_to_file;                         \
        minimum_log_level = min_log_level;                               \
        log_max_message_size = max_message_size;                        \
    } while (0)
